// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "touch_noise_filter/far_apart_taps_filter.h"

#include <base/basictypes.h>

#include <cmath>
#include <cstddef>
#include <cstdio>

namespace touch_noise_filter {

namespace {

// Minimum squared distance between taps to be considered far apart.
__s32 kMinDistance2 = 1500 * 1500;

// Max time between taps considered.
double kMaxTapDeltaSecs = 0.03;

// Maximum squared movement of a touch to still be considered a tap.
__s32 kMaxTapMovement2 = 20 * 20;

// Returns the squared distance between (x1, y1) and (x2, y2).
__s32 Distance2(__s32 x1, __s32 y1, __s32 x2, __s32 y2) {
  return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
}

}  // namespace {}

void FarApartTapsFilter::FilterFrame(Frame* previous, Frame* current,
                                     size_t num_slots) {
  // Remove old taps and touches moving more than min delta.
  double tapCutoff = current->timestamp_ - kMaxTapDeltaSecs;
  for (size_t i = 0; i < num_slots; i++) {
    if (tracked_taps_[i].start_ < tapCutoff)
      tracked_taps_[i].start_ = 0;
  }

  for (size_t slot = 0; slot < num_slots; slot++) {
    Finger* cur = &current->fingers_[slot];
    Finger* prev = &previous->fingers_[slot];

    // Only look at slots with active touches.
    if (cur->tracking_id_ == -1 && prev->tracking_id_ == -1)
      continue;

    bool arrived = prev->tracking_id_ == -1 && cur->tracking_id_ > 0;
    bool departing = prev->tracking_id_ >= 0 && cur->tracking_id_ == -1;

    if (arrived) {
      // Track new finger info.
      tracked_taps_[slot] = Tap(current->timestamp_, cur->x_pos_, cur->y_pos_);
    } else if (tracked_taps_[slot].start_ > 0) {
      // Check if this finger has moved too far to be considered a tap.
      if (kMaxTapMovement2 < Distance2(cur->x_pos_, cur->y_pos_,
              tracked_taps_[slot].x_pos_, tracked_taps_[slot].y_pos_))
        tracked_taps_[slot].start_ = 0;
    }

    if (tracked_taps_[slot].start_ > 0) {
      // Check distance from other tracked taps.
      __s32 min_distance2 = -1;
      for (size_t i = 0; i < num_slots; i++) {
        if (i == slot || tracked_taps_[i].start_ == 0)
          continue;

        __s32 dist2 = Distance2(tracked_taps_[i].x_pos_,
                                tracked_taps_[i].y_pos_,
                                cur->x_pos_, cur->y_pos_);
        if (min_distance2 < 0 || dist2 < min_distance2)
          min_distance2 = dist2;
      }

      if (min_distance2 > kMinDistance2) {
        // The other finger should see this one on its next frame and also
        // get canceled.
        Log("Cancel tracking id %d %.0fpx from other current taps.",
            departing ? prev->tracking_id_ : cur->tracking_id_,
            sqrt(min_distance2));
        cur->canceled_ = true;
      }
    }

    if (departing)
      tracked_taps_[slot].start_ = 0;
  }
}

}  // namespace touch_noise_filter
